Skip to content

feat(slack): Add support for handling message_deleted, message_changed - #549

Closed
mlenczewski-tidio wants to merge 6 commits into
vercel:mainfrom
tidio-tools:feat/add-missing-callbacks
Closed

feat(slack): Add support for handling message_deleted, message_changed#549
mlenczewski-tidio wants to merge 6 commits into
vercel:mainfrom
tidio-tools:feat/add-missing-callbacks

Conversation

@mlenczewski-tidio

@mlenczewski-tidio mlenczewski-tidio commented May 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Chat SDK lifecycle handling for message updates and deletes.

  • Adds core chat APIs for onMessageUpdated, onMessageDeleted, processMessageUpdated, and processMessageDeleted.
  • Dispatches Slack message_changed events as message update callbacks.
  • Dispatches Slack message_deleted events as normalized delete callbacks with message/thread/channel metadata.
  • Preserves existing Slack hidden unfurl behavior so unfurl-only updates are cached without being surfaced as user edits.

Test plan

Slack bot, real webhooks being sent and evaluated.

Checklist

  • All commits are signed and verified
  • pnpm validate passes
  • Changeset added (or N/A — see CONTRIBUTING.md)
  • [N/A] Documentation updated (or N/A)

@mlenczewski-tidio
mlenczewski-tidio requested a review from a team as a code owner May 22, 2026 04:42
@vercel

vercel Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@mlenczewski-tidio is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

@mlenczewski-tidio mlenczewski-tidio changed the title feat: Add support for handling message_deleted and `message_changed… feat: Add support for handling message_deleted, message_changed May 22, 2026
@mlenczewski-tidio
mlenczewski-tidio force-pushed the feat/add-missing-callbacks branch from bf6134d to 4624f49 Compare May 25, 2026 05:19
@mlenczewski-tidio
mlenczewski-tidio force-pushed the feat/add-missing-callbacks branch from 7a52293 to afe0a60 Compare June 11, 2026 14:12
@mlenczewski-tidio mlenczewski-tidio changed the title feat: Add support for handling message_deleted, message_changed feat(slack): Add support for handling message_deleted, message_changed Jun 12, 2026
@mlenczewski-tidio

Copy link
Copy Markdown
Contributor Author

@dancer

@mlenczewski-tidio

Copy link
Copy Markdown
Contributor Author

@bensabic

…e chat prepare rebuildable

Upstream vercel#674/vercel#675 introduced the shared @chat-adapter/tests factories and
matchers, which the rebased adapter-slack tests now use. The mock ChatInstance
did not stub the fork's processMessageUpdated/processMessageDeleted lifecycle
processors, so the message_changed/message_deleted suites failed with
'this.chat.processMessageUpdated is not a function'. Add both processors to
createMockChatInstance and to the toHaveDispatched handler list.

Also make the chat package's prepare script remove dist before tsup: with a
previous build's dist/jsx-runtime.d.ts present, the dts step resolves the
chat/jsx-runtime export to the built file and fails with TS5055 (cannot
overwrite input file). Upstream never hits this because its build script
always cleans first; prepare (used for git installs) did not.
@dancer

dancer commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

closed for #788

@dancer dancer closed this Aug 5, 2026
dancer added a commit that referenced this pull request Aug 5, 2026
## summary

adds `onMessageUpdated` and `onMessageDeleted`, so a bot can react when
a message is edited or removed. Slack dispatches both today; other
adapters can opt in later

supersedes #549, which was verified there against real Slack webhooks.
reopened from a branch in this repo with the original commits preserved
and signed

```typescript
bot.onMessageUpdated(async (thread, message, previousMessage) => {
  await mirror.update(message.id, message.text);
});

bot.onMessageDeleted(async (event) => {
  await mirror.remove(event.messageId);
});
```

both are lifecycle events: they never route through `onNewMessage`,
`onNewMention`, or `onSubscribedMessage`, and the concurrency strategies
do not apply

### notes

- **the bot's own edits are filtered.** slack sends a `message_changed`
for every `chat.update`, and post-and-edit streaming calls it once per
delta, so without this a single streamed reply would call the handler
back repeatedly on its own message
- **`previousMessage` is forwarded on edits.** slack sends the pre-edit
message and it was being dropped. an edit handler usually needs the
before to know what changed, so it is the optional third argument
- **the two shapes differ deliberately.** an edit carries a full
replacement message, so it gets `(thread, message, previousMessage?)`. a
delete has no message, only the id of what was removed, so it gets an
event. use `chat.thread(event.threadId)` when a delete handler needs one
- **one thread id helper** now serves message, edit, and delete, so an
edit cannot resolve to a different thread than the message it edits

## test plan

core:

- an edit dispatches to `onMessageUpdated` and not to the normal message
handlers
- the handler receives the pre-edit message as its third argument
- the bot's own edits are skipped
- a delete dispatches with normalized event data
- both run inside the active conversation, so read tools built in these
handlers stay scoped

slack:

- `message_changed` dispatches as an update, `message_deleted` as a
delete
- `previous_message` is forwarded, and left undefined when slack omits
it
- hidden unfurl updates stay ignored, hidden real edits still dispatch
- message, edit, and delete resolve to one thread id in a flat DM and in
a threaded `agent_view` DM

verified against a real slack workspace over socket mode: editing and
deleting a DM both routed to the same thread id as the original message

---------

Co-authored-by: Miłosz Lenczewski <m.lenczewski@tidio.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants